/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.zq.assistant.utils;

import java.io.File;
import java.io.FileInputStream;
import java.security.MessageDigest;

/**
 * MD5加密工具
 * 
 * @author lyh
 */
public final class MD5Utils {

	private static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	/**
	 * 登录时对密码进行的64位md5加密
	 * 
	 * @param pwd
	 *            密码明文
	 * @return 密码密文
	 */
	public final static String getPassWordMD5(String pwd) {
		String md5  =  getPassWord32MD5(pwd);
		return md5.substring(0, md5.length() / 2);
	}
	
	/**
	 * 登录时对密码进行的64位md5加密
	 * 
	 * @param pwd
	 *            密码明文
	 * @return 密码密文
	 */
	public final static String getPassWord32MD5(String pwd) {
		try {
			byte[] strTemp = pwd.getBytes();
			MessageDigest mdTemp = MessageDigest.getInstance("MD5");
			mdTemp.update(strTemp);
			byte[] md = mdTemp.digest();

			String md5 = messageDigestMD5(md);

			return md5;
			
		} catch (Exception e) {
			return String.valueOf(System.currentTimeMillis());
		}
	}

	/**
	 * 文件128位md5校验（包括文件名） <br>
	 * 当文件大于15M的时候将采用分块模式
	 * 
	 * @param file
	 *            目标文件
	 * @return 文件的128位MD5
	 * 
	 */
	public static String getFileMD5(File file) {
		return getFileMD5(file, false);
	}

	/**
	 * 文件128位md5校验（包括文件名） <br>
	 * 
	 * @param file
	 *            目标文件
	 * @param bool
	 *            当文件大于15M的时候是否对整个文件校验
	 * @return 文件128位md5
	 */
	public static String getFileMD5(File file, boolean bool) {

		if (file == null || !file.exists()) {
			try {
				throw new Exception("要加密的文件有问题");
			} catch (Exception e) {
				e.printStackTrace();
			}

			return String.valueOf(System.currentTimeMillis());
		}

		long len = file.length();
		if (len <= 15 * 1024 * 1024 || bool) {
			return getFileMD5128(file);
		}

		return fileBlockMD5(file);

	}

	/**
	 * 大文件 按分块进行md5校验 <br>
	 * 首尾和中间的5M数据校验
	 * 
	 * @param file
	 *            目标文件
	 * @return
	 */
	private static String fileBlockMD5(File file) {
		FileInputStream fis = null;

		long dataLen = 5 * 1024 * 1024;
		long fileLen = file.length();
		long skip = (fileLen - dataLen * 3) / 2;

		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			fis = new FileInputStream(file);
			byte[] buffer = new byte[8192];
			int length = -1;

			for (int i = 0; i < dataLen / 8192; i++) {

				length = fis.read(buffer);
				md.update(buffer, 0, length);
			}

			fis.skip(skip);
			for (int i = 0; i < dataLen / 8192; i++) {

				length = fis.read(buffer);
				md.update(buffer, 0, length);
			}

			fis.skip(skip);
			for (int i = 0; i < dataLen / 8192; i++) {

				length = fis.read(buffer);
				md.update(buffer, 0, length);
			}
			fis.close();
			return messageDigestMD5(md.digest());

		} catch (Exception e) {
			e.printStackTrace();
		}

		return "";
	}

	/**
	 * 对整个文件进行64位md5校验
	 * 
	 * @param file
	 *            目标文件
	 * @return 如果出错则返回当前系统时间
	 */
	public static String getFileMD564(File file) {
		String md5128 = getFileMD5128(file);
		if (md5128.length() == 32) {
			return md5128.substring(0, md5128.length() / 2);
		}

		return String.valueOf(System.currentTimeMillis());
	}

	/**
	 * 直接对整个文件进行128位md5校验
	 * 
	 * @param file
	 *            目标文件
	 * @return 如果出错则返回当前系统时间
	 */
	public static String getFileMD5128(File file) {
		FileInputStream fis = null;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			fis = new FileInputStream(file);
			byte[] buffer = new byte[8192];
			int length = -1;
			while ((length = fis.read(buffer)) != -1) {
				md.update(buffer, 0, length);
			}

			return messageDigestMD5(md.digest());
		} catch (Exception ex) {
			ex.printStackTrace();
			return String.valueOf(System.currentTimeMillis());
		} finally {
			try {
				fis.close();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 返回文件的SHA校验码
	 * 
	 * @param file
	 *            目标文件
	 * @return 如果出错则返回当前系统时间
	 */
	public static byte[] getFileSHA(File file) {
		FileInputStream fis = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA");
			fis = new FileInputStream(file);
			byte[] buffer = new byte[8192];
			int length = -1;
			while ((length = fis.read(buffer)) != -1) {
				md.update(buffer, 0, length);
			}

			return md.digest();
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			try {
				fis.close();
			} catch (Exception e) {
			}
		}
		return new byte[0];
	}

	/**
	 * 对一段字节数组进行128为MD5加密
	 * 
	 * @param b
	 *            目标数组
	 * @return 如果出错则返回当前系统时间
	 */
	public static String getBytesMD5(byte[] b) {
		return getBytesMD5(b, null);
	}

	/**
	 * 对一段字节数组进行128为MD5加密
	 * @param b
	 * @param key
	 * @return
	 */
	public static String getBytesMD5(byte[] b, byte[] key) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(b);
			if( key != null && key.length > 0 ){
				md.update(key);
			}
			
			return messageDigestMD5(md.digest());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return String.valueOf(System.currentTimeMillis());
	}

	/**
	 * 返回字符串utf-8编码的字节数组的128位md5值
	 * 
	 * @param src
	 *            源字符串
	 * @return 加密后的字符串
	 */
	public static String getStringMD5(String src) {

		try {
			byte[] b = src.getBytes("UTF-8");
			return getBytesMD5(b);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return "";
	}

	/**
	 * 返回一个bytes的md5校验码
	 * 
	 * @param data
	 * @return
	 */
	private static String messageDigestMD5(byte[] data) {

		char[] temp = new char[data.length * 2];
		for (int i = 0; i < data.length; i++) {
			byte b = data[i];
			temp[i * 2] = hexDigits[b >>> 4 & 0x0f];
			temp[i * 2 + 1] = hexDigits[b & 0x0f];
		}
		return new String(temp);
	}
	
}
